"""数据验证工具

提供各种数据验证功能，包括区块链地址、交易哈希、智能合约等验证
"""

import re
import json
from typing import Any, Dict, List, Optional, Union
from datetime import datetime
from .exceptions import ValidationException, SchemaValidationException

# 正则表达式模式
PATTERNS = {
    # 以太坊地址格式 (0x开头，40个十六进制字符)
    'ethereum_address': r'^0x[a-fA-F0-9]{40}$',
    # 比特币地址格式 (简化版)
    'bitcoin_address': r'^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$',
    # 交易哈希 (64个十六进制字符)
    'transaction_hash': r'^0x[a-fA-F0-9]{64}$',
    # 区块哈希 (64个十六进制字符)
    'block_hash': r'^0x[a-fA-F0-9]{64}$',
    # 私钥 (64个十六进制字符)
    'private_key': r'^[a-fA-F0-9]{64}$',
    # 公钥 (130个十六进制字符，04开头)
    'public_key': r'^04[a-fA-F0-9]{128}$',
    # 智能合约地址 (与以太坊地址格式相同)
    'contract_address': r'^0x[a-fA-F0-9]{40}$',
    # 版本号格式 (语义化版本)
    'version': r'^\d+\.\d+\.\d+(-[a-zA-Z0-9]+)?$',
    # 链ID格式
    'chain_id': r'^[a-zA-Z0-9_-]+$',
    # 用户ID格式
    'user_id': r'^[a-zA-Z0-9_-]{1,50}$',
    # API密钥格式
    'api_key': r'^[a-zA-Z0-9]{32,128}$',
    # JWT令牌格式 (简化版)
    'jwt_token': r'^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$',
    # 邮箱格式
    'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
    # 手机号格式 (中国)
    'phone': r'^1[3-9]\d{9}$',
    # IP地址格式
    'ip_address': r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
    # URL格式
    'url': r'^https?://[^\s/$.?#].[^\s]*$'
}

class BaseValidator:
    """基础验证器类"""
    
    def __init__(self, required: bool = True, allow_none: bool = False):
        self.required = required
        self.allow_none = allow_none
    
    def validate(self, value: Any, field_name: str = None) -> Any:
        """验证值"""
        # 检查必填项
        if value is None:
            if self.allow_none:
                return None
            if self.required:
                raise ValidationException(f"Field '{field_name or 'unknown'}' is required", field_name, value)
            return None
        
        # 执行具体验证
        return self._validate_value(value, field_name)
    
    def _validate_value(self, value: Any, field_name: str = None) -> Any:
        """子类需要实现的具体验证逻辑"""
        return value

class StringValidator(BaseValidator):
    """字符串验证器"""
    
    def __init__(self, min_length: int = None, max_length: int = None, 
                 pattern: str = None, **kwargs):
        super().__init__(**kwargs)
        self.min_length = min_length
        self.max_length = max_length
        self.pattern = re.compile(pattern) if pattern else None
    
    def _validate_value(self, value: Any, field_name: str = None) -> str:
        if not isinstance(value, str):
            raise ValidationException(f"Field '{field_name or 'unknown'}' must be a string", field_name, value)
        
        # 长度验证
        if self.min_length is not None and len(value) < self.min_length:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at least {self.min_length} characters",
                field_name, value
            )
        
        if self.max_length is not None and len(value) > self.max_length:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at most {self.max_length} characters",
                field_name, value
            )
        
        # 模式验证
        if self.pattern and not self.pattern.match(value):
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' does not match required pattern",
                field_name, value
            )
        
        return value

class IntegerValidator(BaseValidator):
    """整数验证器"""
    
    def __init__(self, min_value: int = None, max_value: int = None, **kwargs):
        super().__init__(**kwargs)
        self.min_value = min_value
        self.max_value = max_value
    
    def _validate_value(self, value: Any, field_name: str = None) -> int:
        if not isinstance(value, int):
            try:
                value = int(value)
            except (ValueError, TypeError):
                raise ValidationException(f"Field '{field_name or 'unknown'}' must be an integer", field_name, value)
        
        if self.min_value is not None and value < self.min_value:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at least {self.min_value}",
                field_name, value
            )
        
        if self.max_value is not None and value > self.max_value:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at most {self.max_value}",
                field_name, value
            )
        
        return value

class FloatValidator(BaseValidator):
    """浮点数验证器"""
    
    def __init__(self, min_value: float = None, max_value: float = None, **kwargs):
        super().__init__(**kwargs)
        self.min_value = min_value
        self.max_value = max_value
    
    def _validate_value(self, value: Any, field_name: str = None) -> float:
        if not isinstance(value, (int, float)):
            try:
                value = float(value)
            except (ValueError, TypeError):
                raise ValidationException(f"Field '{field_name or 'unknown'}' must be a number", field_name, value)
        
        value = float(value)
        
        if self.min_value is not None and value < self.min_value:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at least {self.min_value}",
                field_name, value
            )
        
        if self.max_value is not None and value > self.max_value:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be at most {self.max_value}",
                field_name, value
            )
        
        return value

class BooleanValidator(BaseValidator):
    """布尔值验证器"""
    
    def _validate_value(self, value: Any, field_name: str = None) -> bool:
        if isinstance(value, bool):
            return value
        
        if isinstance(value, str):
            if value.lower() in ('true', '1', 'yes', 'on'):
                return True
            elif value.lower() in ('false', '0', 'no', 'off'):
                return False
        
        if isinstance(value, int):
            return bool(value)
        
        raise ValidationException(f"Field '{field_name or 'unknown'}' must be a boolean", field_name, value)

class ListValidator(BaseValidator):
    """列表验证器"""
    
    def __init__(self, item_validator: BaseValidator = None, 
                 min_length: int = None, max_length: int = None, **kwargs):
        super().__init__(**kwargs)
        self.item_validator = item_validator
        self.min_length = min_length
        self.max_length = max_length
    
    def _validate_value(self, value: Any, field_name: str = None) -> list:
        if not isinstance(value, list):
            raise ValidationException(f"Field '{field_name or 'unknown'}' must be a list", field_name, value)
        
        # 长度验证
        if self.min_length is not None and len(value) < self.min_length:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must have at least {self.min_length} items",
                field_name, value
            )
        
        if self.max_length is not None and len(value) > self.max_length:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must have at most {self.max_length} items",
                field_name, value
            )
        
        # 验证每个元素
        if self.item_validator:
            validated_items = []
            for i, item in enumerate(value):
                try:
                    validated_item = self.item_validator.validate(item, f"{field_name or 'unknown'}[{i}]")
                    validated_items.append(validated_item)
                except ValidationException as e:
                    raise ValidationException(
                        f"Invalid item at index {i} in field '{field_name or 'unknown'}': {e.message}",
                        field_name, value
                    )
            return validated_items
        
        return value

class DictValidator(BaseValidator):
    """字典验证器"""
    
    def __init__(self, schema: Dict[str, BaseValidator] = None, **kwargs):
        super().__init__(**kwargs)
        self.schema = schema or {}
    
    def _validate_value(self, value: Any, field_name: str = None) -> dict:
        if not isinstance(value, dict):
            raise ValidationException(f"Field '{field_name or 'unknown'}' must be a dictionary", field_name, value)
        
        if not self.schema:
            return value
        
        validated_data = {}
        errors = []
        
        # 验证每个字段
        for key, validator in self.schema.items():
            try:
                field_value = value.get(key)
                validated_data[key] = validator.validate(field_value, key)
            except ValidationException as e:
                errors.append(e.to_dict())
        
        if errors:
            raise SchemaValidationException(
                f"Validation failed for field '{field_name or 'unknown'}'",
                errors
            )
        
        return validated_data

class DateTimeValidator(BaseValidator):
    """日期时间验证器"""
    
    def __init__(self, format_string: str = '%Y-%m-%d %H:%M:%S', **kwargs):
        super().__init__(**kwargs)
        self.format_string = format_string
    
    def _validate_value(self, value: Any, field_name: str = None) -> datetime:
        if isinstance(value, datetime):
            return value
        
        if isinstance(value, str):
            try:
                return datetime.strptime(value, self.format_string)
            except ValueError:
                raise ValidationException(
                    f"Field '{field_name or 'unknown'}' must be a valid datetime in format '{self.format_string}'",
                    field_name, value
                )
        
        raise ValidationException(f"Field '{field_name or 'unknown'}' must be a datetime", field_name, value)

class ChoiceValidator(BaseValidator):
    """选择验证器"""
    
    def __init__(self, choices: List[Any], **kwargs):
        super().__init__(**kwargs)
        self.choices = choices
    
    def _validate_value(self, value: Any, field_name: str = None) -> Any:
        if value not in self.choices:
            raise ValidationException(
                f"Field '{field_name or 'unknown'}' must be one of {self.choices}",
                field_name, value
            )
        return value

# 区块链特定验证器
class BlockchainAddressValidator(StringValidator):
    """区块链地址验证器"""
    
    def __init__(self, chain_type: str = 'ethereum', **kwargs):
        pattern = PATTERNS.get(f'{chain_type}_address', PATTERNS['ethereum_address'])
        super().__init__(pattern=pattern, **kwargs)
        self.chain_type = chain_type

class TransactionHashValidator(StringValidator):
    """交易哈希验证器"""
    
    def __init__(self, **kwargs):
        super().__init__(pattern=PATTERNS['transaction_hash'], **kwargs)

class ContractAddressValidator(StringValidator):
    """智能合约地址验证器"""
    
    def __init__(self, **kwargs):
        super().__init__(pattern=PATTERNS['contract_address'], **kwargs)

class VersionValidator(StringValidator):
    """版本号验证器"""
    
    def __init__(self, **kwargs):
        super().__init__(pattern=PATTERNS['version'], **kwargs)

class ChainIdValidator(StringValidator):
    """链ID验证器"""
    
    def __init__(self, **kwargs):
        super().__init__(pattern=PATTERNS['chain_id'], min_length=1, max_length=50, **kwargs)

# 验证函数
def validate_ethereum_address(address: str) -> bool:
    """验证以太坊地址"""
    return bool(re.match(PATTERNS['ethereum_address'], address))

def validate_transaction_hash(tx_hash: str) -> bool:
    """验证交易哈希"""
    return bool(re.match(PATTERNS['transaction_hash'], tx_hash))

def validate_contract_address(address: str) -> bool:
    """验证智能合约地址"""
    return bool(re.match(PATTERNS['contract_address'], address))

def validate_private_key(private_key: str) -> bool:
    """验证私钥"""
    return bool(re.match(PATTERNS['private_key'], private_key))

def validate_public_key(public_key: str) -> bool:
    """验证公钥"""
    return bool(re.match(PATTERNS['public_key'], public_key))

def validate_version(version: str) -> bool:
    """验证版本号"""
    return bool(re.match(PATTERNS['version'], version))

def validate_chain_id(chain_id: str) -> bool:
    """验证链ID"""
    return bool(re.match(PATTERNS['chain_id'], chain_id))

def validate_email(email: str) -> bool:
    """验证邮箱"""
    return bool(re.match(PATTERNS['email'], email))

def validate_phone(phone: str) -> bool:
    """验证手机号"""
    return bool(re.match(PATTERNS['phone'], phone))

def validate_ip_address(ip: str) -> bool:
    """验证IP地址"""
    return bool(re.match(PATTERNS['ip_address'], ip))

def validate_url(url: str) -> bool:
    """验证URL"""
    return bool(re.match(PATTERNS['url'], url))

def validate_json(json_str: str) -> bool:
    """验证JSON格式"""
    try:
        json.loads(json_str)
        return True
    except (json.JSONDecodeError, TypeError):
        return False

def validate_amount(amount: Union[str, int, float], min_amount: float = 0) -> bool:
    """验证金额"""
    try:
        amount_float = float(amount)
        return amount_float >= min_amount
    except (ValueError, TypeError):
        return False

def validate_gas_limit(gas_limit: Union[str, int]) -> bool:
    """验证Gas限制"""
    try:
        gas_int = int(gas_limit)
        return 21000 <= gas_int <= 10000000  # 合理的Gas范围
    except (ValueError, TypeError):
        return False

def validate_gas_price(gas_price: Union[str, int, float]) -> bool:
    """验证Gas价格"""
    try:
        price_float = float(gas_price)
        return price_float > 0
    except (ValueError, TypeError):
        return False

def validate_nonce(nonce: Union[str, int]) -> bool:
    """验证Nonce"""
    try:
        nonce_int = int(nonce)
        return nonce_int >= 0
    except (ValueError, TypeError):
        return False

# 复合验证器
class TransactionValidator(DictValidator):
    """交易验证器"""
    
    def __init__(self, **kwargs):
        schema = {
            'from': BlockchainAddressValidator(required=True),
            'to': BlockchainAddressValidator(required=True),
            'value': FloatValidator(min_value=0, required=True),
            'gas_limit': IntegerValidator(min_value=21000, max_value=10000000, required=False),
            'gas_price': FloatValidator(min_value=0, required=False),
            'nonce': IntegerValidator(min_value=0, required=False),
            'data': StringValidator(required=False, allow_none=True)
        }
        super().__init__(schema=schema, **kwargs)

class ContractDeploymentValidator(DictValidator):
    """合约部署验证器"""
    
    def __init__(self, **kwargs):
        schema = {
            'contract_name': StringValidator(min_length=1, max_length=100, required=True),
            'contract_code': StringValidator(min_length=1, required=True),
            'constructor_params': ListValidator(required=False, allow_none=True),
            'deployer': BlockchainAddressValidator(required=True),
            'chain_id': ChainIdValidator(required=True),
            'gas_limit': IntegerValidator(min_value=21000, max_value=10000000, required=False),
            'gas_price': FloatValidator(min_value=0, required=False)
        }
        super().__init__(schema=schema, **kwargs)

class UserRegistrationValidator(DictValidator):
    """用户注册验证器"""
    
    def __init__(self, **kwargs):
        schema = {
            'username': StringValidator(min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_-]+$', required=True),
            'email': StringValidator(pattern=PATTERNS['email'], required=True),
            'phone': StringValidator(pattern=PATTERNS['phone'], required=False, allow_none=True),
            'password': StringValidator(min_length=8, max_length=128, required=True),
            'role': ChoiceValidator(choices=['user', 'admin', 'auditor'], required=False)
        }
        super().__init__(schema=schema, **kwargs)

# 批量验证函数
def validate_data(data: dict, validator: DictValidator) -> dict:
    """验证数据"""
    try:
        return validator.validate(data)
    except ValidationException as e:
        raise e
    except Exception as e:
        raise ValidationException(f"Validation failed: {str(e)}")

def validate_request_data(data: dict, schema: dict) -> dict:
    """验证请求数据"""
    validator = DictValidator(schema=schema)
    return validate_data(data, validator)

# 装饰器
def validate_input(schema: dict):
    """输入验证装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            # 假设第一个参数是要验证的数据
            if args:
                validated_data = validate_request_data(args[0], schema)
                args = (validated_data,) + args[1:]
            return func(*args, **kwargs)
        return wrapper
    return decorator